package com.roshan.web.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.roshan.web.dto.DeviceTypeDetail;
import com.roshan.web.dto.DeviceTypeProfile;
import com.roshan.web.dto.ForgotPasswordDetail;
import com.roshan.web.dto.LabelValue;
import com.roshan.web.dto.RegisterUserDetail;
import com.roshan.web.dto.RetrieveCustomerUrisByEmailDetail;
import com.roshan.web.dto.RetrieveCustomerUrisByEmailResult;
import com.roshan.web.dto.RetrievePasswordRecoveryCodeDetail;
import com.roshan.web.dto.RetrievePasswordRecoveryCodeResult;
import com.roshan.web.dto.RetrieveUsernamesByCustomerUrisDetail;
import com.roshan.web.dto.RetrieveUsernamesByCustomerUrisResult;
import com.roshan.web.dto.TenantSecurityQuestionDetail;
import com.roshan.web.dto.TenantSecurityQuestionProfile;
import com.roshan.web.dto.UpdateExpiredPassword;
import com.roshan.web.dto.UpdateExpiredPasswordDetail;
import com.roshan.web.dto.UpdateMFAQuestionProfile;
import com.roshan.web.dto.UpdatePasscode;
import com.roshan.web.dto.UpdatePassword;
import com.roshan.web.dto.UpdateUser;
import com.roshan.web.dto.UpdateUserDetail;
import com.roshan.web.dto.UpdateUserProfile;
import com.roshan.web.dto.UpdateUserSecurityDetail;
import com.roshan.web.dto.UpdateUserSecurityProfile;
import com.roshan.web.dto.User;
import com.roshan.web.dto.UserDetail;
import com.roshan.web.dto.UserProfile;
import com.roshan.web.dto.UserSecurityProfile;
import com.roshan.web.dto.UserUriProfile;
import com.roshan.web.dto.WSCommunicationStatus;
import com.roshan.web.security.exceptions.ResourceNotFoundException;
import com.roshan.web.security.util.constants.SecurityConstants;

/**
 * 
 * @author roshan
 * 
 */
@Service
@SuppressWarnings("nls")
public class UserService implements IUserService {

    @Autowired
    private IEntityManagerService userEntityManagerService;

    @Autowired
    private ISecurityManagerService securityManagerService;

    @Override
    public List<LabelValue> getDeviceTypeList(final String tenant) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public List<LabelValue> getDeviceTypeList(final User user) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public RegisterUserDetail registerUser(final UserProfile userProfile) {

        // TODO remove retrieving all device types
        DeviceTypeProfile deviceTypeProfile = new DeviceTypeProfile();
        deviceTypeProfile.setPartnerProfileUri(userProfile.getPartnerProfileUri());
        deviceTypeProfile.setTenantProfileUri(userProfile.getTenantProfileUri());
        deviceTypeProfile.setStatus(true);

        DeviceTypeDetail deviceTypeDetail = this.userEntityManagerService.retrieveDeviceTypeList(deviceTypeProfile);

        if (WSCommunicationStatus.SUCCESS.equals(deviceTypeDetail.getStatus())
                && !deviceTypeDetail.getDeviceTypeList().isEmpty()) {

            for (LabelValue labelValue : deviceTypeDetail.getDeviceTypeList()) {
                userProfile.getDeviceTypes().add(labelValue.getValue());
            }

        }

        RegisterUserDetail registerUserDetail = this.userEntityManagerService.createCustomer(userProfile);
        return registerUserDetail;
    }

    @Override
    public UserDetail retrieveUser(final String userUri, final String tenantUri, final String partnerUri) {

        UserUriProfile uriProfile = new UserUriProfile();
        uriProfile.setUri(userUri);
        uriProfile.setTenantUri(tenantUri);
        uriProfile.setPartnerUri(partnerUri);

        UserDetail userDetail = this.userEntityManagerService.retrieveUser(uriProfile);
        if (WSCommunicationStatus.SUCCESS.equals(userDetail.getStatus())) {
            return userDetail;
        }
        return null;
    }

    @Override
    public UpdateUserDetail updateUser(final UpdateUser updateUser) {

        DeviceTypeProfile deviceTypeProfile = new DeviceTypeProfile();
        deviceTypeProfile.setPartnerProfileUri(updateUser.getPartnerProfileUri());
        deviceTypeProfile.setTenantProfileUri(updateUser.getTenantProfileUri());
        deviceTypeProfile.setStatus(true);

        DeviceTypeDetail deviceTypeDetail = this.userEntityManagerService.retrieveDeviceTypeList(deviceTypeProfile);

        if (WSCommunicationStatus.SUCCESS.equals(deviceTypeDetail.getStatus())
                && !deviceTypeDetail.getDeviceTypeList().isEmpty()) {

            for (LabelValue labelValue : deviceTypeDetail.getDeviceTypeList()) {
                updateUser.getDeviceTypes().add(labelValue.getValue());
            }
        }

        UpdateUserProfile userProfile = new UpdateUserProfile();
        String userProfileUri = updateUser.getUserProfileUri();
        userProfile.setCustomerUri(userProfileUri);
        userProfile.setDeviceTypes(updateUser.getDeviceTypes());
        userProfile.setEmail(updateUser.getEmail());
        userProfile.setFirstName(updateUser.getFirstName());
        userProfile.setLastName(updateUser.getLastName());
        userProfile.setPartnerProfileUri(updateUser.getPartnerProfileUri());
        userProfile.setPhone(updateUser.getMobile());
        userProfile.setTenantProfileUri(updateUser.getTenantProfileUri());
        userProfile.setUsername(updateUser.getUsername());

        UpdateUserDetail updateUserDetail = this.userEntityManagerService.updateCustomer(userProfile);
        // if (WSCommunicationStatus.SUCCESS.equals(updateUserDetail.getStatus())) {
        // return userProfileUri;
        // }

        return updateUserDetail;
    }

    @Override
    public String updatePassword(final UpdatePassword updatePassword) {

        String errorCode = "roar.customerportal.form.update.password.error";

        UpdateUserSecurityProfile updateUserSecurityProfile = new UpdateUserSecurityProfile();
        updateUserSecurityProfile.setOldPwd(!StringUtils.hasText(updatePassword.getOldPassword()) ? null
                : updatePassword.getOldPassword());
        updateUserSecurityProfile.setNewPwd(!StringUtils.hasText(updatePassword.getPassword()) ? null : updatePassword
                .getPassword());
        updateUserSecurityProfile.setPartnerUri(updatePassword.getPartnerProfileUri());
        updateUserSecurityProfile.setTenantUri(updatePassword.getTenantProfileUri());
        updateUserSecurityProfile.setUserUri(updatePassword.getUserProfileUri());

        UpdateUserSecurityDetail updateUserSecurityDetail = this.securityManagerService
                .updateCustomerSecurityInfo(updateUserSecurityProfile);

        if (WSCommunicationStatus.SUCCESS.equals(updateUserSecurityDetail.getStatus())) {
            return null;

        } else if (WSCommunicationStatus.AUTHENTICATION_FAILED.equals(updateUserSecurityDetail.getStatus())) {
            return "roar.customerportal.form.update.old.password.error";
        } else if (WSCommunicationStatus.CUSTOMER_PASSWORD_IS_PREVIOUSLY_USED.equals(updateUserSecurityDetail
                .getStatus())) {
            return "roar.customerportal.form.update.old.password.previously.used";
        }
        return errorCode;
    }

    @Override
    public String updateExpiredUserPassword(final UpdateExpiredPassword updateExpiredPassword) {

        String errorCode = "roar.customerportal.form.update.password.error";

        UpdateExpiredPasswordDetail updateExpiredPasswordDetail = this.securityManagerService
                .updateExpiredUserPassword(updateExpiredPassword);

        if (WSCommunicationStatus.SUCCESS.equals(updateExpiredPasswordDetail.getStatus())) {
            return null;

        } else if (WSCommunicationStatus.INVALID_PASSWORD.equals(updateExpiredPasswordDetail.getStatus())) {
            return "roar.customerportal.form.update.old.password.error";
        } else if (WSCommunicationStatus.CUSTOMER_PASSWORD_IS_PREVIOUSLY_USED.equals(updateExpiredPasswordDetail
                .getStatus())) {
            return "roar.customerportal.form.update.old.password.previously.used";
        }
        return errorCode;
    }

    @Override
    public String updatePasscode(final UpdatePasscode updatePasscode) {

        String errorCode = "roar.customerportal.form.update.passcode.error";

        WSCommunicationStatus status = this.securityManagerService.updateUserPasscode(updatePasscode);

        if (WSCommunicationStatus.SUCCESS.equals(status)) {
            return null;

        } else if (WSCommunicationStatus.INVALID_DEVICE_PIN.equals(status)) {
            return "roar.customerportal.form.update.old.passcode.error";
        }
        return errorCode;
    }

    @Override
    public List<String> retrieveUserSecurityQuestions(final String userUri, final String tenantUri,
            final String partnerUri) {

        TenantSecurityQuestionProfile tenantSecurityQuestionProfile = new TenantSecurityQuestionProfile();
        tenantSecurityQuestionProfile.setCustomerUri(userUri);
        tenantSecurityQuestionProfile.setPartnerUri(partnerUri);
        tenantSecurityQuestionProfile.setTenantUri(tenantUri);

        TenantSecurityQuestionDetail tenantSecurityQuestionDetail = this.securityManagerService
                .retrieveTenantSecurityQuestions(tenantSecurityQuestionProfile);

        if (!WSCommunicationStatus.SUCCESS.equals(tenantSecurityQuestionDetail.getWsCommunicationStatus())) {
            return null;
        }

        return tenantSecurityQuestionDetail.getAnsweredMfaQuestions();
    }

    @Override
    public boolean updateSecurityQuestions(final Map<String, String> mfaQuestionAnswers,
            final String currentLoginUserUri, final String tenantProfileUri, final String partnerProfileUri) {

        UpdateMFAQuestionProfile updateMFAQuestionProfile = new UpdateMFAQuestionProfile();
        updateMFAQuestionProfile.setUserUri(currentLoginUserUri);
        updateMFAQuestionProfile.setTenantUri(tenantProfileUri);
        updateMFAQuestionProfile.setPartnerUri(partnerProfileUri);

        List<LabelValue> updateSecurityDetail = new ArrayList<LabelValue>();

        for (Map.Entry<String, String> entry : mfaQuestionAnswers.entrySet()) {
            updateSecurityDetail.add(new LabelValue(entry.getKey(), entry.getValue()));
        }

        updateMFAQuestionProfile.setUpdateSecurityDetail(updateSecurityDetail);
        return this.securityManagerService.updateMFAQuestions(updateMFAQuestionProfile);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<UserSecurityProfile> retrieveUsernamesByEmailAddress(final String email, final String tenantUri,
            final String partnerUri) throws RuntimeException, ResourceNotFoundException {

        RetrieveCustomerUrisByEmailDetail retrieveCustomerUrisByEmailDetail = new RetrieveCustomerUrisByEmailDetail();
        retrieveCustomerUrisByEmailDetail.setEmail(email);
        retrieveCustomerUrisByEmailDetail.setPartnerUri(partnerUri);
        retrieveCustomerUrisByEmailDetail.setTenantUri(tenantUri);

        RetrieveCustomerUrisByEmailResult retrieveCustomerUrisByEmailResult = this.userEntityManagerService
                .retrieveCustomerUrisByEmail(retrieveCustomerUrisByEmailDetail);

        if (WSCommunicationStatus.SUCCESS.equals(retrieveCustomerUrisByEmailResult.getStatus())) {

            if (retrieveCustomerUrisByEmailResult.getCustomerUri() == null
                    || (retrieveCustomerUrisByEmailResult.getCustomerUri() != null && retrieveCustomerUrisByEmailResult
                            .getCustomerUri().isEmpty())) {
                throw new ResourceNotFoundException("roar.customerportal.form.forgotusernameinfo.emailnotfound.error");
            }

            RetrieveUsernamesByCustomerUrisDetail retrieveUsernamesByCustomerUrisDetail = new RetrieveUsernamesByCustomerUrisDetail();
            retrieveUsernamesByCustomerUrisDetail.setCustomerUri(retrieveCustomerUrisByEmailResult.getCustomerUri());
            retrieveUsernamesByCustomerUrisDetail.setPartnerUri(partnerUri);
            retrieveUsernamesByCustomerUrisDetail.setTenantUri(tenantUri);

            RetrieveUsernamesByCustomerUrisResult retrieveUsernamesByCustomerUrisResult = this.securityManagerService
                    .retrieveUsernamesByCustomerUris(retrieveUsernamesByCustomerUrisDetail);

            if (WSCommunicationStatus.SUCCESS.equals(retrieveUsernamesByCustomerUrisResult.getStatus())) {

                if (retrieveUsernamesByCustomerUrisResult.getUser().isEmpty()) {
                    throw new ResourceNotFoundException(
                            "roar.customerportal.form.forgotusernameinfo.customernotfound.error");
                }

                return retrieveUsernamesByCustomerUrisResult.getUser();
            }
            throw new RuntimeException("Retrieve usernames by customer uris failed. "
                    + retrieveUsernamesByCustomerUrisResult);

        }
        throw new RuntimeException("Retrieve customer uris by email failed. " + retrieveCustomerUrisByEmailResult);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ForgotPasswordDetail retrieveForgotPasswordRequest(final String partnerUri, final String tenantUri,
            final String userUri, final String type) throws RuntimeException {

        // WS call to create the reset password request
        RetrievePasswordRecoveryCodeDetail retrievePasswordRecoveryCodeDetail = new RetrievePasswordRecoveryCodeDetail();
        retrievePasswordRecoveryCodeDetail.setCustomerUri(userUri);
        retrievePasswordRecoveryCodeDetail.setPartnerUri(partnerUri);
        retrievePasswordRecoveryCodeDetail.setTenantUri(tenantUri);
        retrievePasswordRecoveryCodeDetail.setType(type);

        RetrievePasswordRecoveryCodeResult retrievePasswordRecoveryCodeResult = this.securityManagerService
                .retrievePasswordResetRequest(retrievePasswordRecoveryCodeDetail);

        if (WSCommunicationStatus.SUCCESS.equals(retrievePasswordRecoveryCodeResult.getStatus())) {

            // Get the user details by the user uri
            UserUriProfile userProfileUri = new UserUriProfile();
            userProfileUri.setPartnerUri(partnerUri);
            userProfileUri.setTenantUri(tenantUri);
            userProfileUri.setUri(userUri);
            UserDetail userDetail = this.userEntityManagerService.retrieveUser(userProfileUri);

            if (WSCommunicationStatus.SUCCESS.equals(userDetail.getStatus())) {
                ForgotPasswordDetail forgotPasswordDetail = new ForgotPasswordDetail();
                forgotPasswordDetail.setUserDetail(userDetail);
                forgotPasswordDetail.setCode(retrievePasswordRecoveryCodeResult.getCode());

                return forgotPasswordDetail;
            }
            throw new RuntimeException("Retrieve user failed. " + userDetail);
        }
        throw new RuntimeException("Retrieve password recovery code failed. " + retrievePasswordRecoveryCodeResult);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public UpdateExpiredPasswordDetail retrieveUpdateExpiredPasswordRequest(final String userUri)
            throws RuntimeException {

        RetrievePasswordRecoveryCodeDetail retrievePasswordRecoveryCodeDetail = new RetrievePasswordRecoveryCodeDetail();
        retrievePasswordRecoveryCodeDetail.setCustomerUri(userUri);
        retrievePasswordRecoveryCodeDetail.setType(SecurityConstants.EXPIRED_PASSWORD);

        RetrievePasswordRecoveryCodeResult retrievePasswordRecoveryCodeResult = this.securityManagerService
                .retrievePasswordResetRequest(retrievePasswordRecoveryCodeDetail);

        if (WSCommunicationStatus.SUCCESS.equals(retrievePasswordRecoveryCodeResult.getStatus())) {
            UpdateExpiredPasswordDetail updateExpiredPasswordDetail = new UpdateExpiredPasswordDetail();
            updateExpiredPasswordDetail.setCode(retrievePasswordRecoveryCodeResult.getCode());

            return updateExpiredPasswordDetail;
        }
        throw new RuntimeException("Retrieve password recovery code failed. " + retrievePasswordRecoveryCodeResult);
    }

    @Override
    public Boolean isEmailAddressExists(final String email, final String tenantUri, final String partnerUri,
            final String userUri) {
        RetrieveCustomerUrisByEmailDetail retrieveCustomerUrisByEmailDetail = new RetrieveCustomerUrisByEmailDetail();
        retrieveCustomerUrisByEmailDetail.setEmail(email);
        retrieveCustomerUrisByEmailDetail.setPartnerUri(partnerUri);
        retrieveCustomerUrisByEmailDetail.setTenantUri(tenantUri);

        RetrieveCustomerUrisByEmailResult retrieveCustomerUrisByEmailResult = this.userEntityManagerService
                .retrieveCustomerUrisByEmail(retrieveCustomerUrisByEmailDetail);

        if (WSCommunicationStatus.SUCCESS.equals(retrieveCustomerUrisByEmailResult.getStatus())) {

            if (retrieveCustomerUrisByEmailResult.getCustomerUri() == null
                    || (retrieveCustomerUrisByEmailResult.getCustomerUri() != null && retrieveCustomerUrisByEmailResult
                            .getCustomerUri().isEmpty())) {
                return false;
            } else if (StringUtils.hasText(userUri) && retrieveCustomerUrisByEmailResult.getCustomerUri() != null
                    && !retrieveCustomerUrisByEmailResult.getCustomerUri().isEmpty()) {
                return !retrieveCustomerUrisByEmailResult.getCustomerUri().contains(userUri);
            }

            return true;

        }
        throw new RuntimeException("Retrieve customer uris by email failed. " + retrieveCustomerUrisByEmailResult);
    }

    @Override
    public List<String> isEmailAddressExists(String email, String tenantUri, String partnerUri) {

        RetrieveCustomerUrisByEmailDetail retrieveCustomerUrisByEmailDetail = new RetrieveCustomerUrisByEmailDetail();
        retrieveCustomerUrisByEmailDetail.setEmail(email);
        retrieveCustomerUrisByEmailDetail.setPartnerUri(partnerUri);
        retrieveCustomerUrisByEmailDetail.setTenantUri(tenantUri);

        RetrieveCustomerUrisByEmailResult retrieveCustomerUrisByEmailResult = this.userEntityManagerService
                .retrieveCustomerUrisByEmail(retrieveCustomerUrisByEmailDetail);

        if (!WSCommunicationStatus.SUCCESS.equals(retrieveCustomerUrisByEmailResult.getStatus())) {
            throw new RuntimeException("Retrieve customer uris by email failed. " + retrieveCustomerUrisByEmailResult);

        }
        return retrieveCustomerUrisByEmailResult.getCustomerUri();
    }

}
